using Engine;
using Game;
using GameEntitySystem;
using TemplatesDatabase;
using Random = Game.Random;

namespace SCIENEW {
    public class ComponentBlastFurnace : ComponentInventoryTankBase, IUpdateable {
        public UpdateOrder UpdateOrder => UpdateOrder.Default;

        public ComponentBlockEntity m_componentBlockEntity;
        public SubsystemTerrain m_subsystemTerrain;
        public SubsystemTime m_subsystemTime;
        public SubsystemFluidTransfer m_subsystemFluidTransfer;
        public SubsystemExplosions m_subsystemExplosions;

        public Dictionary<Point3, int> m_ports = new();//浇筑口
        public ComponentFireBox m_componentFireBox;//燃烧室
        public ComponentAirBlower m_componentAirBlower;
        public int m_height = 0;
        public float m_progress = 0;
        int m_previousFluidID = 0;

        public bool IsWorking => IsIntegrity && m_componentFireBox != null && m_componentAirBlower != null && m_componentFireBox.IsPreheating() && m_componentAirBlower.IsWorking();

        public bool IsIntegrity { get; private set; } = false;

        public bool IsPortOpen { get; private set; } = false;

        public int MetalSlotIndex => 0;
        public int CatSlotIndex => 8;

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>(true);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_subsystemTime = Project.FindSubsystem<SubsystemTime>(true);
            m_subsystemFluidTransfer = Project.FindSubsystem<SubsystemFluidTransfer>(true);
            m_subsystemExplosions = Project.FindSubsystem<SubsystemExplosions>(true);
            IsPortOpen = valuesDictionary.GetValue("IsPortOpen", false);

            m_tanks[0].Capacity = 0f;
        }

        public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap) {
            base.Save(valuesDictionary, entityToIdMap);
            valuesDictionary.SetValue("IsPortOpen", IsPortOpen);
        }

        public override int GetSlotCapacity(int slotIndex, int value) {
            if (slotIndex >= MetalSlotIndex
                && slotIndex < CatSlotIndex) {//原料槽
                if (CastingUtils.GetIronItemVolume(value) > 0) {
                    return base.GetSlotCapacity(slotIndex, value);
                }
                return 0;
            }
            if (slotIndex >= CatSlotIndex) {//催化剂槽
                float eff = GetCatEfficiency(value);
                return eff > 0 ? base.GetSlotCapacity(slotIndex, value) : 0;
            }
            return base.GetSlotCapacity(slotIndex, value);
        }

        public void Update(float dt) {
            TerrainChunk chunkAtCell = m_subsystemTerrain.Terrain.GetChunkAtCell(m_componentBlockEntity.Coordinates.X, m_componentBlockEntity.Coordinates.Z);
            if (chunkAtCell != null
                && chunkAtCell.State is TerrainChunkState.Valid or TerrainChunkState.InvalidLight or TerrainChunkState.InvalidPropagatedLight) {
                if (m_subsystemTime.PeriodicGameTimeEvent(0.2f, 0.0f)) {//一秒5次检测
                    CheckIntegrity();
                    UpdateVolume();
                }
            }
            UpdateMelting(dt);
            ReplaceFurnace(m_componentBlockEntity.Coordinates);
            if (m_subsystemTime.PeriodicGameTimeEvent(0.5f, 0.0f)) {
                if(IsIntegrity) UpdateMoltenIron(true);
            }
            UpdateFluidChange();
        }

        public override void OnEntityRemoved() {
            base.OnEntityRemoved();
            foreach (var port in m_ports) {
                if (m_subsystemFluidTransfer.m_elements.TryGetValue(port.Key, out FluidTransferrer? value) && value is BlastPortTransferrer blastPortTransferrer)
                    blastPortTransferrer.ResetBlastFurnace();
            }
            m_ports.Clear();
        }

        void CheckIntegrity() {//检查高炉是否完整
            foreach (var port in m_ports) {
                if (m_subsystemFluidTransfer.m_elements.TryGetValue(port.Key, out FluidTransferrer? value) && value is BlastPortTransferrer blastPortTransferrer)
                    blastPortTransferrer.ResetBlastFurnace();
            }
            m_ports.Clear();
            m_componentFireBox = null;
            m_componentAirBlower = null;
            m_height = 0;

            Point3 corePos = m_componentBlockEntity.Coordinates;
            int coreValue = m_subsystemTerrain.Terrain.GetCellValueFast(corePos.X, corePos.Y, corePos.Z);
            int face = IlluminableDevice.GetDirection_Static(coreValue);//高炉核心朝向
            Point3 centerPos = corePos - CellFace.FaceToPoint3(face);//高炉结构中心的坐标

            //第一层
            for (int x = 0; x < 3; x++) {
                for (int z = 0; z < 3; z++) {
                    Point3 testPos = new Point3(centerPos.X - 1 + x, centerPos.Y - 1, centerPos.Z - 1 + z);
                    int testValue = m_subsystemTerrain.Terrain.GetCellValueFast(testPos.X, testPos.Y, testPos.Z);

                    if (Terrain.ExtractContents(testValue) == BricksBlock.Index) {//检测到砖墙，继续检测下一块
                        continue;
                    }
                    if (Terrain.ExtractContents(testValue) == BaseDeviceBlock.Index && DevicesBlockManager.GetDevice(testValue) is BlastFurnacePortDevice) {//检测到输出口，判断是否可用，然后检测下一块
                        //int portFace = DevicesUtils.GetInnerData(testValue);
                        //Point3 portFacePos1 = testPos + CellFace.FaceToPoint3(portFace);
                        //Point3 portFacePos2 = testPos + CellFace.FaceToPoint3(CellFace.OppositeFace(portFace));
                        //int portSideValue1 = m_subsystemTerrain.Terrain.GetCellValueFast(portFacePos1.X, portFacePos1.Y, portFacePos1.Z);
                        //int portSideValue2 = m_subsystemTerrain.Terrain.GetCellValueFast(portFacePos2.X, portFacePos2.Y, portFacePos2.Z);
                        //if ((Terrain.ExtractContents(portSideValue1) == BaseDeviceBlock.Index && DevicesBlockManager.GetDevice(portSideValue1) is BlastCastingBasinDevice)
                        //    || (Terrain.ExtractContents(portSideValue2) == BaseDeviceBlock.Index && DevicesBlockManager.GetDevice(portSideValue2) is BlastCastingBasinDevice)) {//浇筑口两端有一端空气
                            m_ports.Add(testPos, testValue);//添加可用浇筑口
                            if (m_subsystemFluidTransfer.m_elements[testPos] is BlastPortTransferrer blastPortTransferrer)
                                blastPortTransferrer.AssignBlastFurnace(this);
                        //}
                        continue;
                    }
                    //检测到非法方块
                    IsIntegrity = false;
                    return;
                }
            }
            int[] sideFaces = new int[2];
            int fireBoxSideFace = sideFaces[0];//有燃烧室的那一面
            DevicesUtils.GetSideFaceOfXZ(face, out sideFaces[0], out sideFaces[1]);
            foreach (var sideFace in sideFaces) {//检测燃烧室
                Point3 testPos = centerPos - Point3.UnitY + CellFace.FaceToPoint3(sideFace) * 2;
                if (DeviceEntityUtils.GetDeviceEntity(m_subsystemTerrain, testPos, out ComponentBlockEntity fireBoxEntity)) {//检测到了燃烧室，就停止检测
                    m_componentFireBox = fireBoxEntity.Entity.FindComponent<ComponentFireBox>();
                    if (m_componentFireBox != null) {
                        fireBoxSideFace = sideFace;
                        break;
                    }
                }
            }
            if (m_componentFireBox == null) {//两侧都没有检测到燃烧室，直接判断未成形
                IsIntegrity = false;
                return;
            }

            //第二层
            Point3 backPoint = centerPos - CellFace.FaceToPoint3(face);//核心背对的面
            Point3 rightPoint = centerPos - CellFace.FaceToPoint3(fireBoxSideFace);//鼓风机背对的面
            Point3 leftPoint = centerPos + CellFace.FaceToPoint3(fireBoxSideFace);//鼓风机正对的面
            bool center = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X, centerPos.Y, centerPos.Z) is AirBlock.Index or MoltenIronBlock.Index;
            bool corner1 = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X - 1, centerPos.Y, centerPos.Z - 1) == BricksBlock.Index;//检测四个角
            bool corner2 = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X - 1, centerPos.Y, centerPos.Z + 1) == BricksBlock.Index;
            bool corner3 = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X + 1, centerPos.Y, centerPos.Z + 1) == BricksBlock.Index;
            bool corner4 = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X + 1, centerPos.Y, centerPos.Z - 1) == BricksBlock.Index;
            bool back = m_subsystemTerrain.Terrain.GetCellContentsFast(backPoint.X, backPoint.Y, backPoint.Z) == BricksBlock.Index;
            bool right = m_subsystemTerrain.Terrain.GetCellContentsFast(rightPoint.X, rightPoint.Y, rightPoint.Z) == BricksBlock.Index;
            bool left = m_subsystemTerrain.Terrain.GetCellContentsFast(leftPoint.X, leftPoint.Y, leftPoint.Z) is AirBlock.Index or MoltenIronBlock.Index;
            if (!(center && corner1 && corner2 && corner3 && corner4 && back && right && left)) {//四个角不完全满足是砖块，未成形
                IsIntegrity = false;
                return;
            }
            Point3 airBlowerPoint = centerPos + CellFace.FaceToPoint3(fireBoxSideFace) * 2;//鼓风机位置
            if (DeviceEntityUtils.GetDeviceEntity(m_subsystemTerrain, airBlowerPoint, out ComponentBlockEntity airBlowerEntity)) {//没找到鼓风机，未成形
                m_componentAirBlower = airBlowerEntity.Entity.FindComponent<ComponentAirBlower>(false);
                if (m_componentAirBlower == null) {
                    IsIntegrity = false;
                    return;
                }
            }
            else {
                IsIntegrity = false;
                return;
            }

            //第3~n层
            Point3[] testPoints = [
                new Point3(-1, 0, -1),
                new Point3(-1, 0, 0),
                new Point3(-1, 0, 1),
                new Point3(0, 0, -1),
                new Point3(0, 0, 1),
                new Point3(1, 0, -1),
                new Point3(1, 0, 0),
                new Point3(1, 0, 1),
            ];
            int height = 1;
            while (CheckChimney(testPoints, centerPos + Point3.UnitY * height)) {
                height++;
            }
            height--;
            if (height < 2) {//高度不满足
                IsIntegrity = false;
                return;
            }
            m_height = height;

            //检测流程下来没有任何不符，高炉已成型
            IsIntegrity = true;

            bool CheckChimney(Point3[] points, Point3 center) {
                if (m_subsystemTerrain.Terrain.GetCellContentsFast(center.X, center.Y, center.Z) != AirBlock.Index && m_subsystemTerrain.Terrain.GetCellContentsFast(center.X, center.Y, center.Z) != MoltenIronBlock.Index) return false;
                foreach (var point in points) {
                    Point3 checkPoint = center + point;
                    if (m_subsystemTerrain.Terrain.GetCellContentsFast(checkPoint.X, checkPoint.Y, checkPoint.Z) != BricksBlock.Index) return false;
                }
                return true;
            }
        }

        void UpdateVolume() {
            m_tanks[0].Capacity = MathUtils.Max(m_height - 1, 0) * 36f;
            m_tanks[0].Volume = MathUtils.Min(m_tanks[0].Volume, m_tanks[0].Capacity);
        }

        void UpdateMelting(float dt) {
            float speed = 5;//熔解速度
            float productivity = 1;//产率
            bool haveMetal = m_slots.Take(CatSlotIndex).Any(x => x.Count > 0);
            bool isFull = GetTankVolume(0) >= GetTankCapacity(0, GetTankValue(0));
            if (IsWorking && haveMetal && !isFull) {

                //获取速度
                float eff = CalcCatsEfficiency();
                speed = MathUtils.Lerp(speed, speed + 3, eff);//速度取值~6

                if (m_progress >= 1) {
                    m_progress = 0;
                    int fluidID = FluidsManager.GetFluidID("MoltenIron", 0);

                    //减去一个原料，加对应体积水
                    int slotIndex = -1;
                    float volume = 0;
                    bool isOverFlow = false;
                    while ((GetSlotCount(slotIndex) == 0 || (volume <= 0 || isOverFlow)) && slotIndex < CatSlotIndex) {
                        slotIndex++;

                        if (GetSlotCount(slotIndex) > 0) {
                            int slotValue = GetSlotValue(slotIndex);

                            //遇到矿或废铁，判断催化剂计算产率
                            if (Terrain.ExtractContents(slotValue) == BaseItemBlock.Index) {
                                if (ItemsBlockManager.GetItem(slotValue) is ScrapIronItem) {
                                    productivity = MathUtils.Lerp(0.75f, 0.9f, eff);
                                }else if (ItemsBlockManager.GetItem(slotValue) is OrePowderItem) {
                                    productivity = MathUtils.Lerp(1.5f, 2f, eff);
                                }
                            }

                            volume = CastingUtils.GetIronItemVolume(slotValue) * productivity;
                            isOverFlow = GetTankVolume(0) + volume > GetTankCapacity(0, fluidID);
                        }
                    }
                    if (slotIndex < CatSlotIndex && isOverFlow == false) {//找到了原料
                        m_slots[slotIndex].Count--;
                        AddTankFluid(0, fluidID, volume, out float overflow);

                        //一定概率失去催化剂
                        if (new Random().Float(0f, 1f) < 0.25f) {
                            Slot[] catsSlots = m_slots.Take(new Range(8, 11)).Where(x => x.Count > 0).ToArray();
                            if (catsSlots.Length > 0) {//有催化剂
                                int catIndex = new Random().Int(0, catsSlots.Length - 1);
                                catsSlots[catIndex].Count--;
                            }
                        }
                    }
                }
                else {
                    m_progress += dt * speed;
                }
            }
            else {
                m_progress = 0;
            }
        }

        float GetCatEfficiency(int value) {
            int blockID = Terrain.ExtractContents(value);
            if (blockID == SandBlock.Index) return 0.2f;
            if (blockID == PigmentBlock.Index) return 0.3f;
            if (blockID == BaseItemBlock.Index
                && ItemsBlockManager.GetItem(value).GetCraftingId() == "CoalPowder")
                return 0.5f;
            return 0;
        }

        //计算所以催化剂格中催化剂总效率
        float CalcCatsEfficiency() {
            HashSet<int> cats = m_slots.Take(new Range(8, 11)).Where(x => x.Count > 0).Select(x => x.Value).ToHashSet();
            float eff = 0;//效率，取值0~1
            foreach (var cat in cats) {
                eff += GetCatEfficiency(cat);
            }
            return eff;
        }

        void ReplaceFurnace(Point3 coordinates) {
            TerrainChunk chunkAtCell = m_subsystemTerrain.Terrain.GetChunkAtCell(coordinates.X, coordinates.Z);
            if (chunkAtCell != null
                && chunkAtCell.State == TerrainChunkState.Valid) {
                int cellValue = m_subsystemTerrain.Terrain.GetCellValue(coordinates.X, coordinates.Y, coordinates.Z);
                int direction = IlluminableDevice.GetDirection_Static(cellValue);
                int deviceValue = DevicesBlockManager.GetDeviceValueByType(typeof(BlastFurnaceDevice), IsWorking ? 0b100 | direction : direction);
                m_subsystemTerrain.ChangeCell(coordinates.X, coordinates.Y, coordinates.Z, deviceValue);
            }
        }

        void UpdateMoltenIron(bool placeMode) {
            Point3 corePos = m_componentBlockEntity.Coordinates;
            TerrainChunk chunkAtCell = m_subsystemTerrain.Terrain.GetChunkAtCell(corePos.X, corePos.Z);
            if (chunkAtCell != null
                && chunkAtCell.State is TerrainChunkState.Valid or TerrainChunkState.InvalidLight or TerrainChunkState.InvalidPropagatedLight) {
                int coreValue = m_subsystemTerrain.Terrain.GetCellValueFast(corePos.X, corePos.Y, corePos.Z);
                int face = IlluminableDevice.GetDirection_Static(coreValue); //高炉核心朝向
                Point3 centerPos = corePos - CellFace.FaceToPoint3(face); //高炉结构中心的坐标

                for (int i = 0; i < m_height + 1; i++) {
                    Point3 pos = centerPos + Point3.UnitY * i;
                    float fluidLevel = Math.Clamp(m_tanks[0].Volume - i * 36f, 0f, 36f);
                    if (placeMode) {
                        m_subsystemTerrain.ChangeCell(pos.X, pos.Y, pos.Z, fluidLevel == 0 ? 0 : Terrain.MakeBlockValue(MoltenIronBlock.Index, m_subsystemTerrain.Terrain.GetCellLight(pos.X, pos.Y, pos.Z), FocalorsFluidBlock.SetIsTop(FocalorsFluidBlock.SetLevel(0, 1 - fluidLevel / 36f), true)));
                    }
                    else {
                        m_subsystemTerrain.Terrain.SetCellValueFast(pos.X, pos.Y, pos.Z, fluidLevel == 0 ? 0 : Terrain.MakeBlockValue(MoltenIronBlock.Index, m_subsystemTerrain.Terrain.GetCellLight(pos.X, pos.Y, pos.Z), FocalorsFluidBlock.SetIsTop(FocalorsFluidBlock.SetLevel(0, 1 - fluidLevel / 36f), true)));
                    }
                }
            }
        }

        public void SwitchPortState() {
            IsPortOpen = !IsPortOpen;
        }

        public void UpdateFluidChange() {
            int value = GetTankValue(0);
            if (value != m_previousFluidID) {
                m_previousFluidID = value;
                OnFluidIDChange(value);
            }
        }

        void OnFluidIDChange(int newValue) {
            if (newValue != FluidsManager.GetFluidID("MoltenIron", 0)
                && newValue != 0) {
                ComponentPlayer componentPlayer = FindInteractingPlayer();
                if (componentPlayer != null) {
                    Task.Run(
                        () => {
                            componentPlayer.ComponentGui.DisplayLargeMessage("你发现了彩蛋!", "--- 送你一个小惊喜 ---", 5f, 0f);
                            Thread.Sleep(1000);
                            componentPlayer.ComponentGui.DisplaySmallMessage("5", Color.White, true, true);
                            Thread.Sleep(1000);
                            componentPlayer.ComponentGui.DisplaySmallMessage("4", Color.White, true, true);
                            Thread.Sleep(1000);
                            componentPlayer.ComponentGui.DisplaySmallMessage("3", Color.White, true, true);
                            Thread.Sleep(1000);
                            componentPlayer.ComponentGui.DisplaySmallMessage("2", Color.White, true, true);
                            Thread.Sleep(1000);
                            componentPlayer.ComponentGui.DisplaySmallMessage("1", Color.White, true, true);
                            Thread.Sleep(1000);
                            m_subsystemExplosions.TryExplodeBlock(m_componentBlockEntity.Coordinates.X, m_componentBlockEntity.Coordinates.Y, m_componentBlockEntity.Coordinates.Z, MediumIncendiaryKegBlock.Index);
                        }
                    );
                }
            }
        }
    }
}